<?php
namespace hema\wechat;

/**
 * 微信支付V3 - 报文解密
 *
 */

class AesUtil
{
    /**
	 * AES key
	 * @var string
	 */
	private $aesKey; //ApiV3密钥
	private $error;
	const KEY_LENGTH_BYTE = 32;
	const AUTH_TAG_LENGTH_BYTE = 16;

	/**
	 * 构造函数
	 */
	public function __construct($aesKey) 
	{
		if (strlen($aesKey) != self::KEY_LENGTH_BYTE) {
			$this->error = '无效的ApiV3Key，长度应为32个字节';
			return false;
		}
		$this->aesKey = $aesKey;
	}

	/**
	 * Decrypt AEAD_AES_256_GCM ciphertext
	 *
	 * @param string    $associatedData     AES GCM additional authentication data
	 * @param string    $nonceStr           AES GCM nonce
	 * @param string    $ciphertext         AES GCM cipher text
	 *
	 * @return string|bool      Decrypted string on success or FALSE on failure
	 */
	public function decryptToString($associatedData, $nonceStr, $ciphertext) 
	{
		$ciphertext = \base64_decode($ciphertext);
		if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {
		    $this->error = '待解密报文不合法';
			return false;
		}
		// ext-sodium (default installed on >= PHP 7.2)
		if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') && \sodium_crypto_aead_aes256gcm_is_available()) {
			return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);
		}

		// ext-libsodium (need install libsodium-php 1.x via pecl)
		if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') && \Sodium\crypto_aead_aes256gcm_is_available()) {
			return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);
		}

		// openssl (PHP >= 7.1 support AEAD)
		if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
			$ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
			$authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);
			return \openssl_decrypt($ctext, 'aes-256-gcm', $this->aesKey, \OPENSSL_RAW_DATA, $nonceStr,
				$authTag, $associatedData);
		}
        $this->error = 'AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php';
		return false;
	}
	
	public function getError()
    {
        return $this->error;
    }

}
